package com.hnichr.ump.assessment.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.hnichr.ump.common.assessment.entity.*;
import com.hnichr.ump.common.assessment.service.*;
import com.hnichr.ump.common.assessment.vo.AnswerExcelVO;
import com.hnichr.ump.common.util.MinioUtils;
import com.mxpioframework.jpa.JpaUtil;
import com.mxpioframework.jpa.query.Criteria;
import com.mxpioframework.system.service.impl.BaseServiceImpl;
import io.minio.GetObjectArgs;
import io.minio.GetObjectResponse;
import io.minio.MinioClient;
import io.minio.errors.*;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Service
public class InviteServiceImpl extends BaseServiceImpl<Invite> implements InviteService {

    private static final Logger logger = LoggerFactory.getLogger(InviteServiceImpl.class);

    private static final int BUFFER_SIZE = 2 * 1024;

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private PaperService paperService;

    @Autowired
    private QuestionService questionService;

    @Autowired
    private OptionService optionService;

    @Autowired
    private AnswerTextService answerTextService;

    @Value("${minio.bucket}")
    private String bucket;

    @Transactional(readOnly = true)
    public List<Invite> list(Criteria c) {
        return JpaUtil.linq(Invite.class)
                .collect("id", Testee.class, "testeeId")
                .collect("inviteId", TesteeResult.class, "id")
                .where(c).list();
    }

    @Transactional(readOnly = true)
    public Page<Invite> listPage(Pageable page, Criteria c) {
        return JpaUtil.linq(Invite.class)
                .collect("id", Testee.class, "testeeId")
                .collect("inviteId", TesteeResult.class, "id")
                .where(c)
                .paging(page);
    }

    @Transactional(readOnly = true)
    public void download(String id, OutputStream out) {
        ZipOutputStream zos = null;
        Invite invite = this.getById(Invite.class,id);
        try {
            zos = new ZipOutputStream(out);
            //报告
            reportToZip(zos,invite);
            //本地题目excel
            localPaperAnswerToZip(zos,invite);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (zos != null) {
                try {
                    zos.close();
                } catch (IOException e) {
                }
            }
        }
    }

    private void reportToZip(ZipOutputStream zos,Invite invite) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        List<BaseReport> reportList = new ArrayList<>();
        reportList.addAll(JpaUtil.linq(CompositeReport.class).equal("inviteId", invite.getId()).list());
        reportList.addAll(JpaUtil.linq(TestReport.class).equal("inviteId", invite.getId()).list());
        for (BaseReport report : reportList) {
            String url = report.getLocalUrl();
            if (StringUtils.isBlank(url)) {
                continue;
            }
            String objectName = MinioUtils.getObjectName(url);
            String fileName = MinioUtils.getFileName(url);
            GetObjectResponse objectResponse = minioClient.getObject(GetObjectArgs.builder()
                    .bucket(bucket)
                    .object(objectName)
                    .build());

            byte[] buf = new byte[BUFFER_SIZE];
            zos.putNextEntry(new ZipEntry(fileName));
            int len;
            while ((len = objectResponse.read(buf)) != -1) {
                zos.write(buf, 0, len);
            }
            zos.closeEntry();
            objectResponse.close();
        }
    }

    private void localPaperAnswerToZip(ZipOutputStream zos,Invite invite){
        ExcelWriter excelWriter = null;
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try{
            List<Answer> answerList = JpaUtil.linq(Answer.class).equal("inviteId",invite.getId()).asc("questionOrderNum","optionOrderNum").list();
            if(CollectionUtils.isEmpty(answerList)){
                return;
            }
            Map<String,Question> questionMap = new HashMap<>();
            Map<String,AnswerExcelVO> questionAnswerMap = new HashMap<>();
            int i=1;
            for(Answer answer:answerList){
                String questionId = answer.getQuestionId();
                Question question = null;
                if(questionMap.containsKey(questionId)){
                    question = questionMap.get(questionId);
                }
                else{
                    question = questionService.getById(Question.class,questionId);
                    questionMap.put(questionId,question);
                }


                if(questionAnswerMap.containsKey(questionId)){
                    AnswerExcelVO excelVO = questionAnswerMap.get(questionId);
                    if(StringUtils.isNotBlank(answer.getAnswerdOptionId())){
                        Option option = optionService.getById(Option.class,answer.getAnswerdOptionId());
                        excelVO.setAnswer(excelVO.getAnswer()+";"+(char)10+option.getOptionCode()+":"+option.getOptionName());

                    }
                    else if(StringUtils.isNotBlank(answer.getAnswerdTextId())){
                        AnswerText answerText = answerTextService.getById(AnswerText.class,answer.getAnswerdTextId());
                        excelVO.setAnswer(excelVO.getAnswer()+";"+(char)10+answerText.getAnswerText());
                    }
                }
                else{
                    AnswerExcelVO excelVO = new AnswerExcelVO();
                    questionAnswerMap.put(questionId,excelVO);
                    excelVO.setSerialNo(i++);
                    if(answer.getQuestionOrderNum()==null){
                        excelVO.setQuestionOrderNum(i);
                    }
                    else{
                        excelVO.setQuestionOrderNum(answer.getQuestionOrderNum());
                    }
                    excelVO.setQuestionName(question.getQuestionName());
                    if(StringUtils.isNotBlank(answer.getAnswerdOptionId())){
                        Option option = optionService.getById(Option.class,answer.getAnswerdOptionId());
                        excelVO.setAnswer(option.getOptionCode()+"、"+option.getOptionName());

                    }
                    else if(StringUtils.isNotBlank(answer.getAnswerdTextId())){
                        AnswerText answerText = answerTextService.getById(AnswerText.class,answer.getAnswerdTextId());
                        excelVO.setAnswer(answerText.getAnswerText());
                    }
                }
            }

            List<AnswerExcelVO> excelList = new ArrayList<>(questionAnswerMap.values());
            excelList.sort(Comparator.comparing(AnswerExcelVO::getQuestionOrderNum));

            Paper paper = paperService.getById(Paper.class,answerList.get(0).getPaperId());
            String fileName = paper.getPaperName()+".xlsx";

            zos.putNextEntry(new ZipEntry(fileName));

            ExcelWriterBuilder builder = EasyExcel.write(outputStream).autoCloseStream(false)
                    // 自动适配
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy());
            excelWriter = builder.build();
            WriteSheet writeSheet = EasyExcel.writerSheet(paper.getPaperName()).head(AnswerExcelVO.class).build();
            excelWriter.write(excelList, writeSheet);
            excelWriter.finish();
            outputStream.writeTo(zos);
            zos.closeEntry();
        }
        catch (Exception e){
            throw new RuntimeException(e);
        }
        finally {
            if (excelWriter != null) {
                excelWriter.finish();
            }
            try {
                outputStream.close();
            } catch (IOException e) {
            }
            try {
                zos.closeEntry();
            } catch (IOException e) {
            }
        }

    }
}

